@file:Suppress("MemberVisibilityCanBePrivate", "unused", "SameParameterValue")

package com.virtualightning.anno.processor

import com.squareup.kotlinpoet.ParameterizedTypeName
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.asClassName
import java.io.File
import java.lang.RuntimeException
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Messager
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.*
import javax.lang.model.type.TypeMirror
import javax.tools.Diagnostic

/**
 *  Anchor : Create by CimZzz
 *  Time : 2021/11/22 11:53
 *  注解处理器基类
 */
abstract class BaseProcessor: AbstractProcessor() {
	companion object {
		/**
		 * 配置生成目录文件夹
		 */
		const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"

		/**
		 * 线程池 - 4线程
		 */
		val threadPool: Executor = Executors.newFixedThreadPool(4)
	}

	/**
	 * 环绕环境变量
	 */
	lateinit var roundEnv: RoundEnvironment

	/**
	 * 消息器
	 */
	lateinit var messager: Messager

	/**
	 * 生成文件目录
	 */
	lateinit var dir: File
	lateinit var dir2: File

	/**
	 * 初始化
	 * @param processingEnv ProcessingEnvironment
	 */
	override fun init(processingEnv: ProcessingEnvironment) {
		super.init(processingEnv)
		messager = processingEnv.messager
		val dirPath = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]!!
		dir = File(dirPath)
		dir2 = File(dirPath.replace("kaptKotlin", "kapt"))
	}

	/**
	 * 处理注解
	 * @param p0 MutableSet<out TypeElement>
	 * @param p1 RoundEnvironment
	 * @return Boolean
	 */
	override fun process(p0: MutableSet<out TypeElement>, p1: RoundEnvironment): Boolean {
		roundEnv = p1
		return onProcess(p0)
	}


	/**
	 * 处理注解回调方法
	 * @param elements MutableSet<out TypeElement>
	 * @return Boolean
	 */
	abstract fun onProcess(elements: MutableSet<out TypeElement>): Boolean


	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/11/22 11:58 上午 by CimZzz
	// 以下内容为:
	// 子类快捷处理方法
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 检查元素是否为方法
	 * @param element Element 元素
	 * @return Boolean 如果是返回 true
	 */
	protected fun checkMethod(element: Element?): Boolean {
		return element?.kind == ElementKind.METHOD
	}

	/**
	 * 检查元素是否为属性
	 * @param element Element? 元素
	 * @return Boolean 如果是返回 true
	 */
	protected fun checkField(element: Element?): Boolean {
		return element?.kind == ElementKind.FIELD && element is VariableElement
	}

	/**
	 * 检查元素是否为类
	 * @param element Element 元素
	 * @return Boolean 如果是返回 true
	 */
	protected fun checkClass(element: Element?): Boolean {
		return element?.kind == ElementKind.CLASS && element is TypeElement
	}

	/**
	 * 将指定 Java 类型转换为 Kotlin 类型
	 * @param typeName TypeName 类型名
	 * @return TypeName 类型名
	 */
	protected fun javaTypeToKotlinType(typeName: TypeName): TypeName {
		val typeNameStr = typeName.toString()
		return when {
			typeNameStr == "java.lang.String" -> String::class.asClassName()
			typeNameStr == "java.lang.Int" -> Int::class.asClassName()
			typeNameStr == "java.lang.Integer" -> Int::class.asClassName()
			typeNameStr == "java.lang.Boolean" -> Boolean::class.asClassName()
			typeNameStr == "java.lang.Double" -> Double::class.asClassName()
			typeNameStr.startsWith("java.util.List") -> {
				if(typeName is ParameterizedTypeName) {
					ParameterizedTypeName.get(List::class.asClassName(), *typeName.typeArguments.map { javaTypeToKotlinType(it) }.toTypedArray())
				}
				else {
					List::class.asClassName()
				}
			}
			typeNameStr.startsWith("java.util.HashMap") -> {
				if(typeName is ParameterizedTypeName) {
					ParameterizedTypeName.get(HashMap::class.asClassName(), *typeName.typeArguments.map { javaTypeToKotlinType(it) }.toTypedArray())
				}
				else {
					HashSet::class.asClassName()
				}
			}
			typeNameStr.startsWith("java.util.HashSet") -> {
				if(typeName is ParameterizedTypeName) {
					ParameterizedTypeName.get(HashSet::class.asClassName(), *typeName.typeArguments.map { javaTypeToKotlinType(it) }.toTypedArray())
				}
				else {
					HashSet::class.asClassName()
				}
			}
			typeNameStr == "java.lang.Object" -> Any::class.asClassName()
			typeName is ParameterizedTypeName -> {
				ParameterizedTypeName.get(typeName.rawType, *typeName.typeArguments.map { javaTypeToKotlinType(it) }.toTypedArray())
			}
			else -> typeName
		}
	}

	/**
	 * 获取对应类型的缺省值
	 * @param typeName TypeName 类型名
	 * @return String 缺省值
	 */
	protected fun typeDefaultValue(typeName: TypeName): String {
		val typeNameStr = typeName.toString()
		return when {
			typeNameStr == "kotlin.String" ||
				typeNameStr == "java.lang.String" -> "\"\""

			typeNameStr == "kotlin.Int" ||
				typeNameStr == "java.lang.Int" ||
				typeNameStr == "java.lang.Integer" -> "0"

			typeNameStr == "kotlin.Boolean" ||
				typeNameStr == "java.lang.Boolean" -> "false"

			typeNameStr == "kotlin.Double" ||
				typeNameStr == "java.lang.Double" -> "0.0"

			typeNameStr.startsWith("java.util.List") -> "mutableListOf()"
			typeNameStr.startsWith("kotlin.collections.List") -> "emptyList()"
			typeNameStr.startsWith("kotlin.collections.MutableList") -> "mutableListOf()"
			typeNameStr.startsWith("java.util.Map") -> "emptyMap()"
			typeNameStr.startsWith("java.util.HashMap") -> "HashMap()"
			typeNameStr.startsWith("java.util.Set") -> "emptySet()"
			typeNameStr.startsWith("java.util.HashSet") -> "HashSet()"
			else -> "$typeNameStr()"
		}
	}

	/**
	 * 从 Annotation 中获取 Class 镜像
	 * @param foo VariableElement
	 * @param clazz Class<*>
	 * @param name String
	 * @return TypeMirror?
	 */
	protected fun getAnnotationClass(foo: Element, clazz: Class<*>, name: String): TypeMirror? {
		val clazzName = clazz.name
		val am = foo.annotationMirrors.first {
			it.annotationType.toString() == clazzName
		} ?: return null

		return am.elementValues.firstNotNullOfOrNull {
			val key = it.key
			val value = it.value
			if(key.simpleName.toString() == name) {
				value
			} else {
				null
			}
		}?.value as? TypeMirror
	}

	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/11/22 12:00 下午 by CimZzz
	// 以下内容为:
	// 消息方法
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 发送错误日志
	 */
	protected fun logError(msg: String) {
		val errorContent = "[${javaClass::getName}]: $msg"
		messager.printMessage(Diagnostic.Kind.ERROR, errorContent)
		throw RuntimeException(errorContent)
	}

	/**
	 * 发送输出日志
	 */
	protected fun logInfo(msg: String) {
		val errorContent = "[${javaClass.name}]: $msg"
		messager.printMessage(Diagnostic.Kind.NOTE, errorContent)
	}

	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/11/22 3:03 下午 by CimZzz
	// 以下内容为:
	// 线程池方法
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 线程池执行任务
	 * @param runnable Runnable 执行任务
	 */
	protected fun exec(runnable: Runnable) {
		threadPool.execute(runnable)
	}

}